#include "AUEffectBase.h"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
#include <AudioToolbox/AudioUnitUtilities.h>
#include "FilterVersion.h"
#include "LPFComputator.h" //< myFilter



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma mark ____Filter

class Filter : public AUEffectBase
{
public:
	Filter(AudioUnit component);

	virtual OSStatus			Version() { return kFilterVersion; }

	virtual OSStatus			Initialize();
    
    virtual	OSStatus			GetParameterValueStrings(	AudioUnitScope			inScope,
                                                         AudioUnitParameterID	inParameterID,
                                                         CFArrayRef *			outStrings	);

	virtual OSStatus			GetParameterInfo(	AudioUnitScope			inScope,
													AudioUnitParameterID	inParameterID,
													AudioUnitParameterInfo	&outParameterInfo );
    
	virtual OSStatus			Reset(		AudioUnitScope 				inScope,
                                            AudioUnitElement 			inElement);
    
	virtual OSStatus			ProcessBufferLists(
                                                   AudioUnitRenderActionFlags &	ioActionFlags,
                                                   const AudioBufferList &			inBuffer,
                                                   AudioBufferList &				outBuffer,
                                                   UInt32							inFramesToProcess );
	
	virtual	bool				SupportsTail () { return true; }
    virtual Float64				GetTailTime() {return 0.0;}
    virtual Float64				GetLatency() {return 0.0;}

private:
    LPFComputator* LPFComputator_;

};





//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma mark Standard DSP AudioUnit implementation

AUDIOCOMPONENT_ENTRY(AUBaseFactory, Filter)

enum
{
	kFilterParam_FPass = 0,
	kFilterParam_FStop = 1,
    kFilterParam_OscillationInPass = 2,
    kFilterParam_OscillationInStop = 3,
    kFilterParam_ConvolutionMethod_Indexed = 4
};

static CFStringRef kFPass_Name = CFSTR("frequency pass");
static CFStringRef kFStop_Name = CFSTR("frequency stop");
static CFStringRef kOscillationInPass_Name = CFSTR("oscillation in pass");
static CFStringRef kOscillationInStop_Name = CFSTR("oscillation in stop");
static CFStringRef kConvolutionMethod_Indexed_Name = CFSTR("convolution method");

const float kMinFPass = 12;
const float kDefaultFPass = 3000;

const float kMinFStop = 13;
const float kDefaultFStop = 3250;

const float kMinOscillationInPass = 0.0000001;
const float kMaxOscillationInPass = 0.5;
const float kDefaultOscillationInPass = 0.01 ;

const float kMinOscillationInStop = 0.0000001;
const float kMaxOscillationInStop = 0.5;
const float kDefaultOscillationInStop = 0.01;

const float kMinConvolutionMethod_Indexed = 0;
const float kMaxConvolutionMethod_Indexed = 1;
const float kDefaultConvolutionMethod_Indexed = 0;

//for parameter ConvolutionMethod_Indexed;
static CFStringRef kParameterValueStringsOne   = CFSTR( "CPU" );
static CFStringRef kParameterValueStringsTwo   = CFSTR( "OpenCL GPU" );





//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma mark ____Construction_Initialization

Filter::Filter(AudioUnit component): AUEffectBase(component)
{

	SetParameter(kFilterParam_FPass, kDefaultFPass );
	SetParameter(kFilterParam_FStop, kDefaultFStop );
    SetParameter(kFilterParam_OscillationInPass, kDefaultOscillationInPass );
    SetParameter(kFilterParam_OscillationInStop, kDefaultOscillationInStop );
    SetParameter(kFilterParam_ConvolutionMethod_Indexed, kDefaultConvolutionMethod_Indexed );

	SetParamHasSampleRateDependency(true );
}

OSStatus        Filter::Initialize()
{
	OSStatus result = AUEffectBase::Initialize();
	
	if(result == noErr )    {
        LPFComputator_= new LPFComputator(GetSampleRate(),
                                          GetParameter(kFilterParam_FPass),
                                          GetParameter(kFilterParam_FStop),
                                          GetParameter(kFilterParam_OscillationInPass),
                                          GetParameter(kFilterParam_OscillationInStop),
                                          (LPFComputator::convolutionMethodType)GetParameter(kFilterParam_ConvolutionMethod_Indexed)
                                          //LPFComputator::NORMAL_CPU
                                          );
	}
	
	return result;
}





//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma mark ____Parameters

OSStatus			Filter::GetParameterValueStrings(	AudioUnitScope			inScope,
                                                     AudioUnitParameterID	inParameterID,
                                                     CFArrayRef *			outStrings	)
{
    if ( (inScope == kAudioUnitScope_Global) && (inParameterID == kFilterParam_ConvolutionMethod_Indexed) ) {
        if (outStrings == NULL) return noErr; //called by GetPropInfo
		
		CFStringRef	strings[] = {	kParameterValueStringsOne, kParameterValueStringsTwo };
        
		*outStrings = CFArrayCreate( NULL, (const void **)strings, 2, NULL);
        
        return noErr;
    }
    
    return kAudioUnitErr_InvalidProperty;
}

OSStatus        Filter::GetParameterInfo(	AudioUnitScope			inScope,
												AudioUnitParameterID	inParameterID,
												AudioUnitParameterInfo	&outParameterInfo )
{
	OSStatus result = noErr;

	outParameterInfo.flags = 	kAudioUnitParameterFlag_IsWritable
						+		kAudioUnitParameterFlag_IsReadable;
		
	if (inScope == kAudioUnitScope_Global) {
		
		switch(inParameterID)
		{
            case kFilterParam_FPass:
				AUBase::FillInParameterName (outParameterInfo, kFPass_Name, false);
				outParameterInfo.unit = kAudioUnitParameterUnit_Hertz;
				outParameterInfo.minValue = kMinFPass;
				outParameterInfo.maxValue = GetSampleRate() * 0.5;
				outParameterInfo.defaultValue = kDefaultFPass;
				outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution;
				outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
				break;
                
			case kFilterParam_FStop:
				AUBase::FillInParameterName (outParameterInfo, kFStop_Name, false);
				outParameterInfo.unit = kAudioUnitParameterUnit_Hertz;
				outParameterInfo.minValue = kMinFStop;
				outParameterInfo.maxValue = GetSampleRate() * 0.5;
				outParameterInfo.defaultValue = kDefaultFStop;
				outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution;
				outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
				break;
            
            case kFilterParam_OscillationInPass:
				AUBase::FillInParameterName (outParameterInfo, kOscillationInPass_Name, false);
				outParameterInfo.unit = kAudioUnitParameterUnit_Hertz;
				outParameterInfo.minValue = kMinOscillationInPass;
				outParameterInfo.maxValue = kMaxOscillationInPass;
				outParameterInfo.defaultValue = kDefaultOscillationInPass;
				outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution;
				outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
				break;
            
            case kFilterParam_OscillationInStop:
				AUBase::FillInParameterName (outParameterInfo, kOscillationInStop_Name, false);
				outParameterInfo.unit = kAudioUnitParameterUnit_Hertz;
				outParameterInfo.minValue = kMinOscillationInStop;
				outParameterInfo.maxValue = kMaxOscillationInStop;
				outParameterInfo.defaultValue = kDefaultOscillationInStop;
				outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution;
				outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
				break;
            
            case kFilterParam_ConvolutionMethod_Indexed:
				AUBase::FillInParameterName (outParameterInfo, kConvolutionMethod_Indexed_Name, false);
				outParameterInfo.unit = kAudioUnitParameterUnit_Indexed;
				outParameterInfo.minValue = kMinConvolutionMethod_Indexed;
				outParameterInfo.maxValue = kMaxConvolutionMethod_Indexed;
				outParameterInfo.defaultValue = kDefaultConvolutionMethod_Indexed;
				break;
							
			default:
				result = kAudioUnitErr_InvalidParameter;
				break;
		}
	} else {
		result = kAudioUnitErr_InvalidParameter;
	}
	
	return result;
}





//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#pragma mark ____Processing

OSStatus        Filter::Reset(		AudioUnitScope 				inScope,
                                  AudioUnitElement 			inElement)
{
    LPFComputator_->reset();
    return noErr;
}

OSStatus        Filter::ProcessBufferLists(
                                               AudioUnitRenderActionFlags &	ioActionFlags,
                                               const AudioBufferList &			inBuffer,
                                               AudioBufferList &				outBuffer,
                                               UInt32							inFramesToProcess )
{
    unsigned int channelsNum = GetNumberOfChannels();
    float_type** in, **out;
    in = new float_type*[channelsNum];
    out = new float_type*[channelsNum];
    
    for(int i=0; i < inBuffer.mNumberBuffers; ++i) {
        in[i] = (float_type*) inBuffer.mBuffers[i].mData;
        out[i] = (float_type*) outBuffer.mBuffers[i].mData;
    }
    LPFComputator_->setParameters(GetSampleRate(),
                                  GetParameter(kFilterParam_FPass),
                                  GetParameter(kFilterParam_FStop),
                                  GetParameter(kFilterParam_OscillationInPass),
                                  GetParameter(kFilterParam_OscillationInStop),
                                  (LPFComputator::convolutionMethodType)GetParameter(kFilterParam_ConvolutionMethod_Indexed)
                                  //LPFComputator::NORMAL_CPU
                                  );
    
    LPFComputator_->process(in, out, inFramesToProcess, inBuffer.mNumberBuffers);
    
    return noErr;
}

















